package com.caigou.security.config;

import com.caigou.bean.cms.entity.Privilege;
import com.caigou.bean.cms.entity.UserBaseInfo;
import com.caigou.bean.exam.entity.Users;
import com.caigou.component.RedisComponent;
import com.caigou.enums.Constant;
import com.caigou.enums.ResponseEnum;
import com.caigou.exception.ForbiddenAccountException;
import com.caigou.exception.UserNotFoundException;
import com.caigou.mapper.cms.PrivilegeMapper;
import com.caigou.mapper.cms.UserBaseInfoMapper;
import com.caigou.mapper.exam.UserInfoMapper;
import com.caigou.mapper.exam.UsersMapper;
import com.caigou.security.entity.JwtUser;
import com.caigou.security.exception.JWTAccessDeniedHandler;
import com.caigou.security.exception.JWTAuthenticationEntryPoint;
import com.caigou.security.filter.JWTAuthenticationFilter;
import com.caigou.security.filter.JWTAuthorizationFilter;
import com.caigou.util.Md5Util;
import com.caigou.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.List;

@Slf4j
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsServiceImpl")
    private UserDetailsService userDetailsService;
    @Resource
    private PrivilegeMapper privilegeMapper;
    @Resource
    private RedisComponent redisUtil;
    @Resource
    private UserInfoMapper userInfoMapper;
    @Resource
    private UsersMapper usersMapper;
    @Resource
    private UserBaseInfoMapper userBaseInfoMapper;

    //配置认证用户和权限
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence orgPaswword) {
                return null;
            }

            @Override
            public boolean matches(CharSequence orgPaswword, String dbPassworld) {
                //若没有查询到用户信息则验证失败
                if (StringUtils.isEmpty(dbPassworld)) {
                    return false;
                }
                //由于只能接收密码一个字段,所以将盐值放入密码中使用字符串分割使用
                String[] split = dbPassworld.split("##");
                String password = Md5Util.MD5_32bit(Md5Util.MD5_32bit((String) orgPaswword, "UTF-8") + split[1], "UTF-8");
                //return split[0].equals(password);
                //判断用户短信验证是否正确
                String redisSmsKey = Constant.SMS_PRE_KEY + split[2];
                String redisSmsCode = (String) redisUtil.get(redisSmsKey);
                if (split[0].equals(password)) {
                    JwtUser userDetails = (JwtUser) userDetailsService.loadUserByUsername(split[2]);

                    Users user = usersMapper.findUserByUserId(userDetails.getUserId());
                    if (user.getUserStatus() == 0) {
                        throw new ForbiddenAccountException(ResponseEnum.FORBIDDEN_ACCOUNT);
                    }
                    UserBaseInfo userBaseInfo = userBaseInfoMapper.findUserBaseInfoByUserId(userDetails.getUserId());
                    if (userBaseInfo != null) {
                        if (userBaseInfo.getStatus() == 0) {
                            throw new ForbiddenAccountException(ResponseEnum.FORBIDDEN_ACCOUNT);
                        }
                    }

                    userInfoMapper.updateUserLoginNum(userDetails.getUserId());
                    return true;
                } else {
                    if (orgPaswword.equals(redisSmsCode)) {
                        JwtUser userDetails = (JwtUser) userDetailsService.loadUserByUsername(split[2]);
                        Users user = usersMapper.findUserByUserId(userDetails.getUserId());
                        if (user.getUserStatus() == 0) {
                            throw new ForbiddenAccountException(ResponseEnum.FORBIDDEN_ACCOUNT);
                        }
                        UserBaseInfo userBaseInfo = userBaseInfoMapper.findUserBaseInfoByUserId(userDetails.getUserId());
                        if (userBaseInfo != null) {
                            if (userBaseInfo.getStatus() == 0) {
                                throw new ForbiddenAccountException(ResponseEnum.FORBIDDEN_ACCOUNT);
                            }
                            userInfoMapper.updateUserLoginNum(userDetails.getUserId());
                        }

                    }
                    return orgPaswword.equals(redisSmsCode);
                }
            }
        });


    }

    //配置拦截请求资源
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = http.authorizeRequests();
        //查询数据库中所以权限
        List<Privilege> privilegesAll = privilegeMapper.findAllPrivileges();
        Privilege loginPrivilege = new Privilege();
        loginPrivilege.setRouterUrl("user/login");
        loginPrivilege.setPrivilegeCode("user_login");
        privilegesAll.add(loginPrivilege);
        if (!CollectionUtils.isEmpty(privilegesAll)) {
            //将权限放入security中
            privilegesAll.forEach(privilege -> {
                authorizeRequests.antMatchers(privilege.getPrivilegeCode()).hasAnyAuthority(privilege.getPrivilegeCode());
            });
        }

        //配置security访问规则
        http.cors().configurationSource(CorsConfigurationSource()).and().csrf().disable()
                .authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
//        http.cors().and().csrf().disable()
//                .authorizeRequests()

                // 测试用资源，需要验证了的用户才能访问
                //.antMatchers("/user/sendSmsCode","/user/register","/user/forgot/password","/","/dic/**","/es/**","/esItem/**","/deal/**").permitAll()
                //.antMatchers("/user/sendSmsCode","/user/register","/","/dic/**","/es/**","/esItem/**").authenticated()
                //.anyRequest().authenticated()//任何没有匹配上的其他的url请求，只需要用户被验证
                // 其他都放行了


                .anyRequest().permitAll()
                .and()


                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                // 不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)


                //是否统使用JWT进行统一处理
                .and()
                .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
                .accessDeniedHandler(new JWTAccessDeniedHandler())

        ;


    }

    /*跨域原*/
    private CorsConfigurationSource CorsConfigurationSource() {
        log.info("===============================cors config /auth/login=============");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");    //同源配置，*表示任何请求都视为同源，若需指定ip和端口可以改为如“localhost：8080”，多个以“，”分隔；
        corsConfiguration.addAllowedHeader("*");//header，允许哪些header，本案中使用的是token，此处可将*替换为token；
        corsConfiguration.addAllowedMethod("*");    //允许的请求方法，POST、GET等
        source.registerCorsConfiguration("/auth/login", corsConfiguration); //配置允许跨域访问的url
        return source;
    }
}


